package com.airwallex.strategy.impl;

import com.airwallex.util.constant.ProjectConstants;
import com.airwallex.util.exception.CalculateException;
import com.airwallex.model.ExpParam;
import com.airwallex.strategy.CalculateStrategy;
import com.airwallex.util.CalculatorUtils;
import com.airwallex.util.exception.InsufficientParamException;
import org.apache.commons.math3.special.Gamma;

import java.math.BigDecimal;
import java.util.List;
import java.util.Stack;

/**
 * 阶乘
 */
public class FactorialStrategy implements CalculateStrategy {
    @Override
    public void execCalc(Stack<ExpParam> numbers, Stack<List<ExpParam>> logs) {
        if (numbers.size() > 0) {
            BigDecimal opnd = numbers.pop().getNum();
            BigDecimal result = generalFactorial(opnd);
            result = result.setScale(ProjectConstants.RESULT_SCALE, ProjectConstants.ROUNDING_MODE);
            ExpParam resultParam = ExpParam.builder()
                    .content(result.toString())
                    .num(result)
                    .pos(-1)
                    .isNumber(true)
                    .build();
            numbers.push(resultParam);
            CalculatorUtils.addLog(numbers, logs);
        } else {
            throw new InsufficientParamException("insucient parameters !");
        }
    }

    //广义阶乘
    public BigDecimal generalFactorial(BigDecimal n) {
        /**
         * 广义阶乘定义
         * n! = F(n+1)，F为伽马函数
         * 递推：F(n+1) = nF(n),n+1>1
         * 余元公式：F(n)F(1-n) = pi / sin (s * pi)，0<n<1
         */
        double value = n.doubleValue() + 1.00D;
        //用伽马函数
        return new BigDecimal(Gamma.gamma(value));
    }
}
